﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using MovieModel.Model.Common;
using MovieModel.Search;

namespace MovieBuddy.Controls.MSTB
{
    /// <summary>
    /// Interaction logic for MultiSelectTextBox.xaml
    /// </summary>
    public partial class MultiSelectTextBox
    {
        private const double RowHeight = 32.0;
        private readonly List<MSTBItem> items = new List<MSTBItem>();
        private readonly AutoCompleteBox textBoxSave;
        
        
        public MultiSelectTextBox()
        {

            InitializeComponent();
            SizeChanged += SizeChangedHandler;
            textBoxSave = textBox;
            ItemBackground = Brushes.LightBlue;
            MinTextBoxWidth = 60;

            textBox.KeyDown += TextBoxKeyDown;
            textBox.ItemFilter = SearchItems;
            textBox.LostFocus += TextBoxLostFocus;
            textBox.DropDownClosed += TextBoxDropDownClosed;
            textBox.BackspaceDown += TextBoxBackspaceDown;
        }

        void TextBoxBackspaceDown(object obj)
        {
            if (textBox.Text.Length == 0 && items.Count > 0)
            {
                items.RemoveAt(items.Count - 1);
                Reflow();
                return;
            }
        }

        void TextBoxDropDownClosed(object sender, RoutedPropertyChangedEventArgs<bool> e)
        {
            if (textBox.SelectedItem != null)
            {
                var movieItem = textBox.SelectedItem as MovieItem;
                if (movieItem == null) return;
                var item = new MultiSelectTextBoxItem { Text = movieItem.Name, Id = movieItem.Id };
                textBox.Text = "";
                AddItem(item);
                Reflow();
            }
        }

        void TextBoxLostFocus(object sender, RoutedEventArgs e)
        {
            if (textBox.Text.Length > 0) AddItemInTextBox();
        }

        static bool SearchItems(string search, object value)
        {
            var item = value as MovieItem;
            if (item == null) return false;
            return item.Matches(new SearchQuery {Free = new[] {search.Trim()}});
        }

        void TextBoxKeyDown(object sender, KeyEventArgs e)
        {
            bool isComplete = false;
            if (e.Key == Key.OemSemicolon || e.Key == Key.Enter || e.Key == Key.OemComma)
            {
                isComplete = true;
            }
            if (e.Key == Key.Tab && textBox.Text.Trim() != "")
            {
                isComplete = true;
            }
            if (!isComplete) return;

            AddItemInTextBox();
            e.Handled = true;
        }

        private void AddItemInTextBox()
        {
            string text = textBox.Text.Trim();
            MultiSelectTextBoxItem item = null;
            if (ItemsSource != null)
            {
                item = ItemsSource.Where(i => i.Name.Equals(text, StringComparison.InvariantCultureIgnoreCase))
                    .Select(i => new MultiSelectTextBoxItem { Text = i.Name, Id = i.Id }).FirstOrDefault();
            }
            if (item == null)
            {
                item = new MultiSelectTextBoxItem { Text = text };
            }
            textBox.Text = "";
            AddItem(item);
            Reflow();
        }

        void SizeChangedHandler(object sender, SizeChangedEventArgs e)
        {
            Reflow();
        }

        private void Reflow()
        {
            canvas.Children.Clear();
            if (ActualWidth <= 15.0) return; // not wide enough to draw things
            double x = 0, y = 0;
            foreach (var item in items)
            {
                PlaceItem(item, ref x, ref y);
            }
            PlaceTextBox(ref x, ref y);
            Height = y + RowHeight;
        }

        public Brush ItemBackground { get; set; }
        public int MinTextBoxWidth { get; set; }
        private IEnumerable<MovieItem> itemsSource;

        public IEnumerable<MovieItem> ItemsSource
        {
            get { return itemsSource; }
            set
            {
                textBox.ItemsSource = value;
                itemsSource = value;
            }
        }

        public IEnumerable<MultiSelectTextBoxItem> Items
        {
            get 
            {
                return items.Select(i => i.Item);
            }
            set
            {
                foreach (var v in value)
                {
                    AddItem(v);
                }
                Reflow();
            }
        }

        void AddItem(MultiSelectTextBoxItem item)
        {
            if (item.Id != null)
            {
                var toRemove = items.FirstOrDefault(i => i.Item.Id == item.Id);
                if (toRemove != null) items.Remove(toRemove);
            }
            else
            {
                var toRemove =
                    items.FirstOrDefault(
                        i =>
                        i.Item.Id == null && i.Item.Text.Equals(item.Text, StringComparison.InvariantCultureIgnoreCase));
                if (toRemove != null) items.Remove(toRemove);
            }

            var control = new MSTBItem { Item = item, ItemBackground = ItemBackground };
            control.RemoveButtonClicked += ItemRemoveButtonClicked;
            items.Add(control);
        }

        void ItemRemoveButtonClicked(object sender, ItemRemoveButtonEventArgs e)
        {
            items.Remove(e.Item);
            Reflow();
        }

        private void PlaceItem(MSTBItem item, ref double x, ref double y)
        {
            canvas.Children.Add(item);
            item.FixWidth();
            double itemWidth = item.ActualWidth;

            // if it's not at the start of a row and the item is too wide, start a new row
            if (x > .2 * ActualWidth && x + itemWidth > ActualWidth)
            {
                y += RowHeight;
                x = 0;
            }
            item.SetValue(Canvas.LeftProperty, x);
            item.SetValue(Canvas.TopProperty, y);

            x += item.ActualWidth + 6.0;
        }

        private void PlaceTextBox(ref double x, ref double y)
        {
            if (x > .2 * ActualWidth && x + MinTextBoxWidth > ActualWidth)
            {
                y += RowHeight;
                x = 0;
            }
            canvas.Children.Add(textBoxSave);
            textBoxSave.SetValue(Canvas.LeftProperty, x);
            textBoxSave.SetValue(Canvas.TopProperty, y + 2.0);
            textBoxSave.Width = Math.Max(0, ActualWidth - x - 10);
            x += textBoxSave.Width;
        }
    }
}
